/*
    Copyright (C) 2008  DiGital <digital7@ukr.net>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

    This program contains code from GNU Wget,
    released under the GNU GPL (http://gnu.org/).
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MALLOC_CHECK_ = 1
#define countof(A) sizeof(A)/sizeof((A)[0])

struct DATASTRUCT {int code; char *name; char *command;};
 
extern char *config_file;
extern char *procname;
extern struct DATASTRUCT data[];

enum parse_line {
 line_ok,
 line_empty,
 line_syntax_error,
 line_unknown_command
};

char *
read_whole_line (FILE *fp)
{
	int length = 0;
	int bufsize = 82;
	char *line = (char *)malloc (bufsize);

	while (fgets (line + length, bufsize - length, fp))
	{
		length += strlen (line + length);
		if (length == 0)
	/* Possible for example when reading from a binary file where
			a line begins with \0.  */
			continue;

		if (line[length - 1] == '\n')
			break;

      /* fgets() guarantees to read the whole line, or to use up the
		space we've given it.  We can double the buffer
		unconditionally.  */
		bufsize <<= 1;
		line = realloc (line, bufsize);
	}
	if (length == 0 || ferror (fp))
	{
		free (line);
		return NULL;
	}
	if (length + 1 < bufsize)
    /* Relieve the memory from our exponential greediness.  We say
		`length + 1' because the terminating \0 is not included in
		LENGTH.  We don't need to zero-terminate the string ourselves,
		though, because fgets() does that.  */
		line = realloc (line, length + 1);
	return line;
}

char *
strdupdelim (const char *beg, const char *end)
{
	char *res = (char *)malloc (end - beg + 1);
	memcpy (res, beg, end - beg);
	res[end - beg] = '\0';
	return res;
}

int
command_by_name (const char *cmdname)
{
  /* Use binary search for speed.  Wget has ~100 commands, which
	guarantees a worst case performance of 7 string comparisons.  */
	int lo = 1, hi = data[0].code - 1, mid;
/*	while (lo <= hi)
	{
		int mid = (lo + hi) >> 1;
		printf ("%s - %s : %d\n", cmdname, data[mid].name, mid);
		int cmp = strcasecmp (cmdname, data[mid].name);
		if (cmp < 0)
			hi = mid - 1;
		else if (cmp > 0)
			lo = mid + 1;
		else 
			return mid;
	}	*/

	for (mid = lo; mid <= hi; mid++)
	{
		if (strcasecmp (cmdname, data[mid].name) == 0)
			return mid;
	}
	return -1;
}

static enum parse_line
parse_line (const char *line, char **com, char **val, int *com_ind)
{
	const char *p;
	const char *end = line + strlen (line);
	const char *cmdstart, *cmdend;
	const char *valstart, *valend;

	/* Skip leading and trailing whitespace.  */
	while (*line && isspace (*line))
		++line;
	while (end > line && isspace (end[-1]))
		--end;

	/* Skip empty lines and comments.  */
	if (!*line || *line == '#')
		return line_empty;

	p = line;

	cmdstart = p;
	while (p < end && (isalnum (*p) || *p == '_' || *p == '-'))
		++p;
	cmdend = p;

	/* Skip '=', as well as any space before or after it. */
	while (p < end && isspace (*p))
		++p;
	if (p == end || *p != '=')
		return line_syntax_error;
	++p;
	while (p < end && isspace (*p))
		++p;

	valstart = p;
	valend   = end;

  /* The syntax is valid (even though the command might not be).  Fill
	in the command name and value.  */
	*com = strdupdelim (cmdstart, cmdend);
	*val = strdupdelim (valstart, valend);

	*com_ind = command_by_name(*com);
	if (*com_ind == -1)
		return line_unknown_command;
	
	return line_ok;
}

int
set_value(int *command_index, char *value)
{
	data[*command_index].command = (char *)malloc (sizeof(char) * (strlen(value)+1));
	strcpy (data[*command_index].command, value);
	return 0;
}

int
get_settings ()
{
	FILE *fp;
	char *line;
	int ln;
	int errcnt = 0;
	
	if ((fp=fopen(config_file, "r")) > 0)
	{
		ln = 1;
		while ((line = read_whole_line (fp)) != NULL)
		{
			char *com = NULL, *val = NULL;
			int com_ind;
			
			/* Parse the line.  */
			switch (parse_line (line, &com, &val, &com_ind))
			{
				case line_ok:
					/* If everything is OK, set the value.  */
					set_value(&com_ind, val);
					break;
				case line_syntax_error:
					fprintf (stderr, "%s: Syntax error in configuration file at line %d.\n", procname, ln);
					++errcnt;
					break;
				case line_unknown_command:
					fprintf (stderr, "%s: Unknown command in configuration file at line %d.\n", procname, ln);
					++errcnt;
					break;
				case line_empty:
					break;
				default:
					abort ();
			}
			free (com);
			free (val);
			free (line);
			++ln;
		}
		fclose (fp);
	} else { 
		fprintf (stderr, "%s: Can not open configuration file!\n", procname);
		++errcnt;
	}
	return errcnt;
}

